package kibahed.university.effizientealgorithmen.gui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.HashMap;

import javax.swing.JFrame;
import javax.swing.JLabel;

import kibahed.university.effizientealgorithmen.interfaces.IAlgorithm;
import kibahed.university.effizientealgorithmen.interfaces.IEdge;
import kibahed.university.effizientealgorithmen.interfaces.IGraph;
import kibahed.university.effizientealgorithmen.interfaces.IVertex;
import kibahed.university.effizientealgorithmen.interfaces.IVertexPlanarDouble;
import kibahed.university.effizientealgorithmen.interfaces.IVertexPlanarInteger;

public class GraphVizualisation extends JLabel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static int scaleFactor = 1000;
	private static int offset = 400;
	private double vertexScale = 1;
	IGraph graph = null;
	HashMap<Integer, Point2D.Double> points = null;
	AffineTransform currentMatrix = new AffineTransform();
	boolean firstTime = true;
	boolean showShortestPath = true;
	private boolean showNodeTour = true;
	private StartFrame startFrame = null;

	public void setGraph(IGraph graph, StartFrame startFrame) {
		this.graph = graph;
		this.startFrame = startFrame;
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		if (graph != null) {

			int counter = 0;
			Graphics2D g2d = (Graphics2D) g;
			g2d.setTransform(currentMatrix);
			if (graph.getEdges().size() < 5000) {
				for (IEdge edge : graph.getEdges()) {
					Point2D.Double position = ((IVertexPlanarDouble) graph
							.getVertexes()[edge.getStart()])
							.getPositionDouble();
					Point2D.Double position2 = ((IVertexPlanarDouble) graph
							.getVertexes()[edge.getEnd()]).getPositionDouble();
					g2d.setColor(Color.LIGHT_GRAY);
					g2d.drawLine((int) position.getX() * scaleFactor,
							(int) position.getY() * scaleFactor,
							(int) position2.getX() * scaleFactor,
							(int) position2.getY() * scaleFactor);
				}
			}

			for (IVertex vertex : graph.getVertexes()) {
				Point2D.Double position = null;
				if (vertex instanceof IVertexPlanarDouble) {
					position = ((IVertexPlanarDouble) vertex)
							.getPositionDouble();
				} else if (vertex instanceof IVertexPlanarInteger) {
					position = new Point2D.Double(
							((IVertexPlanarInteger) vertex).getX(),
							((IVertexPlanarInteger) vertex).getY());
				}
				if (position != null) {
					if (!vertex.isMarked()) {
						g2d.setColor(Color.BLACK);
					} else {
						g2d.setColor(Color.GREEN);
					}
					g2d.fillOval((int) (position.getX() * scaleFactor
							- scaleFactor*vertexScale), (int) (position.getY() * scaleFactor
							- scaleFactor*vertexScale), (int)(2 * scaleFactor*vertexScale),(int) (2 * scaleFactor*vertexScale));
				}
			}

			if (showShortestPath) {
				int count = startFrame.getAlgorithms().size();
				for (IAlgorithm algorithm : startFrame.getAlgorithms()) {
					g2d.setStroke(new BasicStroke(300));
					int size = startFrame.getAlgorithms().size();
					int red = (int) (((size - count + 2) % 3 / 2)
							* (1 - count / ((double) size + 10)) * 255);
					int green = (int) (((size - count + 0) % 3 / 2)
							* (1 - count / ((double) size + 10)) * 255);
					int blue = (int) (((size - count + 1) % 3 / 2)
							* (1 - count / ((double) size + 10)) * 255);
					g2d.setColor(new Color(red, green, blue, 255));
					// g2d.setColor(Color.RED);

					if (algorithm.getResult() != null) {
						for (IEdge edge : algorithm.getResult()) {
							IVertex vertex = graph.getVertexes()[edge
									.getStart()];
							IVertex vertex2 = graph.getVertexes()[edge.getEnd()];
							Point2D.Double position = null;
							Point2D.Double position2 = null;
							if (vertex instanceof IVertexPlanarDouble) {
								position = ((IVertexPlanarDouble) vertex)
										.getPositionDouble();
								position2 = ((IVertexPlanarDouble) vertex2)
										.getPositionDouble();
							} else if (vertex instanceof IVertexPlanarInteger) {
								position = new Point2D.Double(
										((IVertexPlanarInteger) vertex).getX(),
										((IVertexPlanarInteger) vertex).getY());
								position2 = new Point2D.Double(
										((IVertexPlanarInteger) vertex2).getX(),
										((IVertexPlanarInteger) vertex2).getY());
							}

							if (position != null && position2 != null) {
								int realOffset = (int) (((size - count + 1) / 2) * Math
										.signum(1 - 2 * ((size - count) % 2)));
								g2d.drawLine((int) position.getX()
										* scaleFactor + offset * realOffset,
										(int) position.getY() * scaleFactor
												+ offset * realOffset,
										(int) position2.getX() * scaleFactor
												+ offset * realOffset,
										(int) position2.getY() * scaleFactor
												+ offset * realOffset);
							}
						}

					}
					g2d.setStroke(new BasicStroke(1));
					count--;
				}
			}

			if (showNodeTour) {

				g2d.setStroke(new BasicStroke(200));
				if (graph.getNodeTour() != null) {
					g2d.setColor(Color.GREEN);
					for (int i = 0; i < graph.getNodeTour().length; i++) {
						int node1 = graph.getNodeTour()[i];
						IVertex vertex = graph.getVertexes()[node1];
						Point2D.Double position = null;
						if (vertex instanceof IVertexPlanarDouble) {
							position = ((IVertexPlanarDouble) vertex)
									.getPositionDouble();
						} else if (vertex instanceof IVertexPlanarInteger) {
							position = new Point2D.Double(
									((IVertexPlanarInteger) vertex).getX(),
									((IVertexPlanarInteger) vertex).getY());
						}
						if (position != null) {
							IVertex vertex2;

							if (i + 1 < graph.getNodeTour().length) {
								int node2 = graph.getNodeTour()[i + 1];
								vertex2 = graph.getVertexes()[node2];
							} else {
								int node2 = graph.getNodeTour()[0];
								vertex2 = graph.getVertexes()[graph
										.getNodeTour()[node2]];
							}
							Point2D.Double position2 = null;
							if (vertex2 instanceof IVertexPlanarDouble) {
								position2 = ((IVertexPlanarDouble) vertex2)
										.getPositionDouble();
							} else if (vertex2 instanceof IVertexPlanarInteger) {
								position2 = new Point2D.Double(
										((IVertexPlanarInteger) vertex2).getX(),
										((IVertexPlanarInteger) vertex2).getY());
							}
							if (position2 != null) {

								g2d.drawLine((int) position.getX()
										* scaleFactor, (int) position.getY()
										* scaleFactor, (int) position2.getX()
										* scaleFactor, (int) position2.getY()
										* scaleFactor);

							}
						}
					}
				}
				g2d.setStroke(new BasicStroke(1));
			}

		}
	}

	public void addZoomListener(JFrame frame) {

		KeyListener listener = new KeyListener() {

			@Override
			public void keyTyped(KeyEvent arg0) {

			}

			@Override
			public void keyReleased(KeyEvent arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void keyPressed(KeyEvent arg0) {

				if (arg0.getKeyCode() == 107 || arg0.getKeyCode() == 521) { // PLUS
					currentMatrix.scale(1.2, 1.2);
				} else if (arg0.getKeyCode() == 109 || arg0.getKeyCode() == 45) { // MINUS
					currentMatrix.scale(1 / 1.2, 1 / 1.2);
				} else if (arg0.getKeyCode() == KeyEvent.VK_UP) {
					currentMatrix.translate(0, -10 / currentMatrix.getScaleY());
				} else if (arg0.getKeyCode() == KeyEvent.VK_DOWN) {
					currentMatrix.translate(0, 10 / currentMatrix.getScaleY());
				} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
					currentMatrix.translate(10 / currentMatrix.getScaleX(), 0);
				} else if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
					currentMatrix.translate(10 / -currentMatrix.getScaleX(), 0);
				} else if(arg0.getKeyCode() == KeyEvent.VK_MULTIPLY) {
					vertexScale *= 1.5;
				} else if(arg0.getKeyCode() == KeyEvent.VK_DIVIDE) {
					vertexScale /= 1.5;
				}
				updateUI();
			}
		};
		currentMatrix.scale(0.01, 0.01);
		frame.addKeyListener(listener);
	}

}
